package redtankface;

import redtankface.tasks.*;
import robocode.BulletHitEvent;
import robocode.HitByBulletEvent;
import robocode.Robot;
import robocode.ScannedRobotEvent;

import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

/**
 * RedTankFace
 */
public class RedTankFace extends Robot {
	private final Map<String, EnemyInfo> mEnemies = new HashMap<String, EnemyInfo>();

	private final List<TankTask> mTasks = new LinkedList<TankTask>();

	/**
	 * Список ударов
	 */
	private final List<HitInfo> mHits = new LinkedList();

	/**
	 * Минимальный угол, на который есть смысл поворачиваться
	 */
	private final static double MIN_ANGLE = 0.05;

	/**
	 * Время последнего обнаружения противника
	 */
	private long mLastDetect = 0;

	public void run() {
		try {
			System.out.println("!!! Redface started !!!");

			mTasks.add(new ScanTask(this));
			mTasks.add(new FireTask(this));
			mTasks.add(new FullScanTask(this));
			mTasks.add(new AttackMoveTask(this, Math.PI/6));
			mTasks.add(new AttackMoveTask(this, -Math.PI/6));
			mTasks.add(new AttackMoveTask(this, Math.PI/4));
			mTasks.add(new AttackMoveTask(this, -Math.PI/4));
			mTasks.add(new RunAwayTask(this, Math.PI/6));
			mTasks.add(new RunAwayTask(this, -Math.PI/6));
			mTasks.add(new RunAwayTask(this, Math.PI/4));
			mTasks.add(new RunAwayTask(this, -Math.PI/4));
			mTasks.add(new RotateOpposite(this));


			// Сразу же делаем полное сканирование, чтобы знать, кто где.
			turnRadarRight(360);


			while (true) {
				// Выбираем самое оптимальное действие и делаем его
				long max = 0;
				TankTask bestTask = null;
				for (TankTask task : mTasks) {
					long rating = task.getRating();
					if (max < rating) {
						bestTask = task;
						max = rating;
					}
					System.out.println("   for task " + task.getClass().getName() + " rating is: " + rating);
				}
				System.out.println("Best task: " + bestTask.getClass().getName());
				bestTask.doTask();
			}
		} catch (Throwable ex) {
			ex.printStackTrace();
		}
	}

	public Map<String, EnemyInfo> getEnemies() {
		return mEnemies;
	}

	/**
	 * При сканировании робота просто запоминаем информацию о нем.
	 */
	@Override
	public void onScannedRobot(ScannedRobotEvent e) {
		EnemyInfo info = mEnemies.get(e.getName());
		if (info == null) {
			info = new EnemyInfo(e, this, getTime(), e.getName());
			mEnemies.put(e.getName(), info);
		} else {
			info.updateData(e, this, getTime());
		}

		mLastDetect = getTime();
		ScanTask.dropScanSet();
		ScanTask.setLastScanned(info);
	}

	@Override
	public void onBulletHit(BulletHitEvent event) {
		EnemyInfo info = mEnemies.get(event.getName());
		if (info != null) {
			info.recordHit();
		}
	}

	@Override
	public void onHitByBullet(HitByBulletEvent e) {
		mHits.add(new HitInfo(getTime(), e));
	}

	/**
	 * Возвращает список попаданий по нам
	 * @return
	 */
	public List<HitInfo> getHists() {
		return mHits;
	}

	public TankCoordinates getCoord() {
		TankCoordinates coord = new TankCoordinates();
		coord.mX = getX();
		coord.mY = getY();
		return coord;
	}

	public void fireEnemy(EnemyInfo aEnemy) {
		aEnemy.recordFire();

		double clearAngle = TankUtils.getAngle(getCoord(), aEnemy.getCoord(getTime()));
		double gunAngle = Math.toRadians(getGunHeading());
		double angle = clearAngle - gunAngle;
		double distance = TankUtils.getDistance(getCoord(), aEnemy.getCoord(getTime()));
		double turnRightAngle = robocode.util.Utils.normalRelativeAngleDegrees(Math.toDegrees(angle));

		if (Math.abs(turnRightAngle) > MIN_ANGLE) {
			turnGunRight(turnRightAngle);
		}

		int power = 1;
		if (distance < 200)
			power = 2;
		if (distance < 100)
			power = 3;
		if (distance < 50)
			power = 4;
		fire(power);

		if (Math.abs(turnRightAngle) > MIN_ANGLE) {
			turnRadarLeft(turnRightAngle);
		}
	}

	/**
	 * Возвращает время последнего срабатывания радара
	 */
	public long getLastDetect() {
		return mLastDetect;
	}

	/**
	 * Выполняет поворот к заданным координатам
	 */
	public void doTurnTank(TankCoordinates aCoordinates, double aAjustment) {
		double heading = TankUtils.getAngle(getCoord(), aCoordinates);
		double angle = robocode.util.Utils.normalRelativeAngle(heading - Math.toRadians(getHeading()) + aAjustment);
		turnRight(Math.toDegrees(angle));
	}
}
